﻿using System.Data;
using System.Diagnostics;
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

using BodaNetCommunications.Cores;
using BodaNetCommunications.S7Coms.S7Utils;
using BodaNetCommunications.Utilities.ComTypes;
using BodaNetCommunications.Utilities.Results;


namespace BodaNetCommunications.S7Coms
{
    /// <summary>
    /// 西门子S7-300、S7-400 和 S7-1200/1500 是大端序(Big-Endian)，S7-1200/1500 标准块为大端序，优化的块为小端序(Little-Endian)
    /// </summary>
    public class S7Plc : ICommDevice
    {
        public ConnectionOptions Options { get; private set; }

        private ICommUnit _commUnit;

        private int _pduSize = 240;

        public bool IsConnected => _commUnit != null && _commUnit.IsConnected;



        private Dictionary<string, S7PValueSize> _pValudDic = new Dictionary<string, S7PValueSize>()
        {
            { "X",S7PValueSize.BIT},
            { "B",S7PValueSize.BYTE},
            { "W",S7PValueSize.WORD},
            { "D",S7PValueSize.DWORD},
            { "R",S7PValueSize.REAL},
        };

        private Dictionary<S7PValueSize, Type> _pValudTypeDic = new Dictionary<S7PValueSize, Type>()
        {
            { S7PValueSize.BIT, typeof(bool)},
            { S7PValueSize.BYTE, typeof(byte)},
            { S7PValueSize.WORD, typeof(short)},
            { S7PValueSize.DWORD, typeof(int)},
            { S7PValueSize.REAL, typeof(float)},
            { S7PValueSize.DINT, typeof(int)},
        };

        private Dictionary<string, S7Areas> _areasDic = new Dictionary<string, S7Areas>()
        {
            { "I",S7Areas.Input},
            { "Q",S7Areas.Output},
            { "M",S7Areas.Memery},
            { "V",S7Areas.DataBlock},
        };

        /// <summary>
        /// 西门子的plc，必须传入机架号和槽位号
        /// </summary>
        /// <param name="connectionOptions"></param>
        /// <exception cref="Exception"></exception>
        public S7Plc()
        {

        }

        public BodaResult Connect(ConnectionOptions connectionOptions)
        {
            BodaResult bodaResult = new BodaResult();
            Options = connectionOptions;
            Options.PlcType = McPLCType.McPlc;

            if (string.IsNullOrEmpty(Options.Ip) || false == IPAddress.TryParse(Options.Ip, out _))
            {
                bodaResult.IsSuccess = false;
                bodaResult.Message = $"Ip[{Options.Ip}]为空或者格式不正确";
                return bodaResult;
            }

            if (Options.Port < 0 || Options.Port > 65535)
            {
                bodaResult.IsSuccess = false;
                bodaResult.Message = $"Port[{Options.Port}]不正确";
                return bodaResult;
            }

            if (IsConnected)
            {
                bodaResult.IsSuccess = false;
                bodaResult.Message = $"S7【{Options.Ip}:{Options.Port}】的连接已经在线，不需要重复连接";
                return bodaResult;
            }

            _commUnit = new CommUnitS7(Options.Ip.Trim(), Options.Port);

            // 连接分步
            BodaResult<bool> result = _commUnit.Open(Options.ConnectTimeout);
            if (!result.IsSuccess) return result;


            if (string.IsNullOrEmpty(connectionOptions.PlcName))
            {
                bodaResult.IsSuccess = false;
                bodaResult.Message = $"S7【{Options.Ip}:{Options.Port}】的PLC名称为空";
                return bodaResult;
            }


            // COTP，传入机架号和槽位号
            result = COTP(Options.Rack, Options.Slot);
            if (!result.IsSuccess)
            {
                result.Message = $"【COTP】错误信息-> {result.Message}";
                return result;
            }

            // SetupCommunication，获取PDU
            result = SetupCommunication();
            if (!result.IsSuccess)
            {
                result.Message = $"【SetupCommunication】错误信息-> {result.Message}";
                return result;
            }

            return result;
        }

        public void Disconnect()
        {
            _commUnit?.Close();
        }

        private BodaResult<bool> COTP(byte rack, byte slot)
        {
            // 1、报文中有没有需要动态计算的？
            //  有：机架号/插槽号
            // 2、需要将Ethernet对象接入进来
            //  ICommUnit处理连接
            // 3、关于响应报文的处理：
            //  必须处理？PLC肯定会有响应, 存在缓冲区; 如果不做处理，则下次报文会出现错误
            // 4、对于握手是否成功如何判断？
            //  异步捕获

            List<byte> bytes = new List<byte>();
            // TPKT
            bytes.Add(0x03);
            bytes.Add(0x00);
            bytes.Add(0x00);
            bytes.Add(0x16);
            // COTP
            bytes.Add(0x11);//   10#17
            bytes.Add(0xe0);

            bytes.Add(0x00);
            bytes.Add(0x00);
            bytes.Add(0x00);
            bytes.Add(0x00);
            bytes.Add(0x00);

            bytes.Add(0xc0);
            bytes.Add(0x01);
            bytes.Add(0x0a);

            bytes.Add(0xc1);   // 源设备（上位机PC）通信配置      S7协议   西门子设备之间也是要使用
            bytes.Add(0x02);
            bytes.Add(0x01);   //[17]
            bytes.Add(0x00);   //[18]  00-02

            bytes.Add(0xc2);   // PLC   
            bytes.Add(0x02);
            bytes.Add(0x03);   // [21] 03-01
            bytes.Add((byte)(rack * 32 + slot));   // [22] 机架号0  插槽号1

            BodaResult<byte> result = _commUnit.SendAndReceive(bytes);// 返回响应
            if (!result.IsSuccess) return new BodaResult<bool> { IsSuccess = false, Message = result.Message };

            return new BodaResult<bool>();
        }

        private BodaResult<bool> SetupCommunication()
        {
            List<byte> bytes = new List<byte>();
            // TPKT
            bytes.Add(0x03);
            bytes.Add(0x00);
            bytes.Add(0x00);
            bytes.Add(0x19);
            // COTP
            bytes.Add(0x02);
            bytes.Add(0xf0);
            bytes.Add(0x80);   // 1000 0000
            // S7-Header
            bytes.Add(0x32);
            bytes.Add(0x01);

            bytes.Add(0x00);
            bytes.Add(0x00);
            bytes.Add(0x00);
            bytes.Add(0x00);

            bytes.Add(0x00);
            bytes.Add(0x08);// Paremeter部分字节长度

            bytes.Add(0x00);
            bytes.Add(0x00);// Data部分字节长度

            // S7-Parameter
            bytes.Add(0xf0);// Function
            bytes.Add(0x00);

            bytes.Add(0x00);
            bytes.Add(0x03);// Calling

            bytes.Add(0x00);
            bytes.Add(0x03);// Called

            bytes.Add(0x03);
            bytes.Add(0xc0);// PDU

            BodaResult<byte> result = _commUnit.SendAndReceive(bytes);// 返回响应
            if (!result.IsSuccess) return new BodaResult<bool> { IsSuccess = false, Message = result.Message };

            if (result.Datas[17] != 0x00)
                return new BodaResult<bool> { IsSuccess = false, Message = "报文异常" };

            // 知道PLC的PDU长度
            _pduSize = BitConverter.ToUInt16(new byte[] { result.Datas[26], result.Datas[25] });
            Console.WriteLine("PDU Size:" + _pduSize);

            return new BodaResult<bool>();
        }



        // VW0   DB1.DBW0   M0.0   I  Q   Length
        // 一个Item    
        // Byte[]
        public BodaResult<T> Read<T>(S7Areas area, ushort db, S7PValueSize pValueSize, int byteAddr, ushort count, byte bitAddr = 0)
        {
            BodaResult<T> result = new BodaResult<T>();

            S7DataParameter parameter = new S7DataParameter();
            parameter.Area = area;
            parameter.DBNumber = db;
            parameter.PValueSize = pValueSize;
            parameter.Count = count;
            parameter.ByteAddress = byteAddr;
            parameter.BitAddress = bitAddr;
            parameter.DataType = typeof(T);

//#if DEBUG
//            Stopwatch sw = Stopwatch.StartNew();
//#endif
            BodaResult reqResult = this.Read(new List<S7DataParameter> { parameter });
//#if DEBUG
//            Console.WriteLine($"Read<T> Time2: {sw.ElapsedMilliseconds}ms");
//            sw.Restart();
//#endif
           
            if (!reqResult.IsSuccess)
            {
                result.IsSuccess = false;
                result.Message = reqResult.Message;
            }
            else
            { 
                foreach (var item in parameter.Datas)
                {
                    //dynamic v = item;
                    result.Datas.Add((T)item);
                }
                //parameter.Datas.ForEach(d =>
                //{
                //    dynamic v = d;
                //    result.Datas.Add(v);
                //});
            }
//#if DEBUG
//            Console.WriteLine($"Read<T> Time3: {sw.ElapsedMilliseconds}ms");
//            sw.Stop();
//#endif
            return result;
        }

        private List<byte> GetParameterItem(S7Areas areas, ushort db, S7PValueSize pTransportSize, int byteAddr, ushort count, byte bitAddr)
        {
            List<byte> items = new List<byte>();
            items.Add(0x12);

            items.Add(0x0a);
            items.Add(0x10);

            items.Add((byte)pTransportSize);

            items.Add((byte)(count / 256));
            items.Add((byte)(count % 256));

            items.Add((byte)(db / 256));
            items.Add((byte)(db % 256)); // DB编号

            items.Add((byte)areas);

            // 地址计算
            byteAddr = (byteAddr << 3) + bitAddr;

            items.Add((byte)(byteAddr / 256 / 256 % 256));
            items.Add((byte)(byteAddr / 256 % 256));
            items.Add((byte)(byteAddr % 256));

            return items;
        }

        /// <summary>
        /// 用于pdu剩余的安全字符长度
        /// </summary>
        private const int pduSizeOffset = 40;


        /// <summary>
        /// 第一步：先获取能够一次请求的地址以及长度
        /// 第二步：根据第一次的结果 进行一下次的任务组装 
        ///   三
        ///   四
        /// 效果：每调用一次，获取相就的地址以及长度
        ///       在Read方法中处理完成后，判断进行下一次的请求，也就是再调用一次这个方法
        ///       
        /// 例 ： VW10 - 10个模拟量        VW100 - 200 
        ///       DataParameter   
        ///       10byte - 100量
        ///       210byte - 80量
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="index"></param>
        /// <param name="doneCount">当前parameters[index]中的已处理量</param>
        /// <returns></returns>
        private BodaResult<S7DataParameter> GroupAddress(List<S7DataParameter> parameters, ref int index, ref int doneCount)
        {
            BodaResult<S7DataParameter> result = new BodaResult<S7DataParameter>();

            int byteCount = 0;// 意思：指是当前这个分组中已经有多个字节

            try
            {
                for (int i = index; i < parameters.Count; i++)
                {
                    S7DataParameter dataParameter = new S7DataParameter();

                    int size = Marshal.SizeOf(parameters[i].DataType);// Bool   != 1
                    if (parameters[i].DataType == typeof(bool))
                    {
                        size = 1;
                    }

                    dataParameter.Id = parameters[i].Id;
                    dataParameter.Area = parameters[i].Area;
                    dataParameter.DBNumber = parameters[i].DBNumber;
                    dataParameter.BitAddress = parameters[i].BitAddress; // BIT    1Byte
                    dataParameter.PValueSize = parameters[i].PValueSize;
                    dataParameter.DataType = parameters[i].DataType;

                    // 第一次进来
                    // 第二次进来   需要知道前一次处理的量
                    dataParameter.ByteAddress = parameters[i].ByteAddress + doneCount * size;
                    dataParameter.Count = (ushort)(parameters[i].Count - doneCount);

                    // 判断长度
                    // 指当前Parameter的请求长度在dataParameter的PDU范围内
                    if ((byteCount + (parameters[i].Count - doneCount) * size) <= (_pduSize - pduSizeOffset))
                    {
                        // 当前Parameter剩余长度
                        byteCount += (parameters[i].Count - doneCount) * size;
                        // 新的分组对象添加到返回集合中，以待数据处理
                        result.Datas.Add(dataParameter);
                    }
                    else
                    {
                        // Parameter的请求长度超出了dataParameter允许范围；
                        //      但是dataParameter还有空间可以处理，将parameters[i]中部分数据加入到当前的dataParameter中
                        if ((_pduSize - pduSizeOffset - byteCount) > 0)
                        {
                            // 原因：保证返回的字节与数据类型匹配   
                            // 所以 X / size * size 的作用， 比如：10个字节   float 4    8 => 10 / 4 = 2 -> 2*4 =8 返回8
                            ushort len = (ushort)((_pduSize - pduSizeOffset - byteCount) / size * size);
                            byteCount += len;
                            dataParameter.Count = (ushort)(len / size);// 当前临时对象只能请求部分数据

                            doneCount += dataParameter.Count;// 已处理过的数据量
                            result.Datas.Add(dataParameter);
                        }

                        break;
                    }


                    index++;
                    // 当前parameters[i]已经处理完，则需要清空
                    doneCount = 0;
                }

            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.Message = ex.Message;
            }
            return result;
        }

        /// <summary>
        /// 需求，一次性把所有需求地址丢进来请求，一次性返回所有结果
        /// 1、解决多个区域地址的同时处理    List（区域编码  PSize  ByteAddr）
        ///    S7DataParameter 里包含：区域编码  PSize  ByteAddr Count  BitAddr  返回类型  返回数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public BodaResult Read(List<S7DataParameter> parameters)
        {
            BodaResult result = new BodaResult() { IsSuccess = true, Message = string.Empty };

            try
            {
                int index = 0;
                int doneCount = 0;
                while (index < parameters.Count)
                {
                    var groupResult = this.GroupAddress(parameters, ref index, ref doneCount);
                    if (!groupResult.IsSuccess) return new BodaResult { IsSuccess = false, Message = groupResult.Message };

                    List<byte> reqBytes = new List<byte>();
                    // TPKT
                    reqBytes.Add(0x03);
                    reqBytes.Add(0x00);
                    // 初始化无意义，只做点位，后续做修改  ，注意第161行
                    reqBytes.Add(0x00);
                    reqBytes.Add(0x00);// 注意下后台再修改---
                                       // COTP
                    reqBytes.Add(0x02);
                    reqBytes.Add(0xf0);
                    reqBytes.Add(0x80);   // 1000 0000
                                          // S7-Header
                    reqBytes.Add(0x32);
                    reqBytes.Add(0x01);// ROSCTR

                    reqBytes.Add(0x00);
                    reqBytes.Add(0x00);
                    reqBytes.Add(0x00);
                    reqBytes.Add(0x00);

                    #region Parameter部分
                    List<byte> paramBytes = new List<byte>();
                    paramBytes.Add(0x04);
                    paramBytes.Add((byte)groupResult.Datas.Count); /// 长度超过PDU长度   

                    // 拼接所有Item

                    /// 思路：
                    /// 1、有多个连续地址，比如  I0.0  VW10-200   MB10-1
                    /// 2、每个地址以及请求的长度计算一个响应总字节数    遍历
                    /// 3、如果某一个地址超长了，先处理这个地址中部分
                    ///    部分如何计算：PDU - 已经有的（count）= 当前这个地址可以请求的字节数
                    ///    最终获取的字节数必须与类型相符
                    foreach (var item in groupResult.Datas)
                    {
                        List<byte> pi = GetParameterItem(
                            item.Area,
                            item.DBNumber,
                            item.PValueSize,
                            item.ByteAddress,
                            item.Count,
                            item.BitAddress);
                        paramBytes.AddRange(pi);
                    }
                    #endregion

                    reqBytes.Add((byte)(paramBytes.Count / 256));
                    reqBytes.Add((byte)(paramBytes.Count % 256));// Paremeter部分字节长度。注意后面修改    14   26   1A     38  26

                    reqBytes.Add(0x00);
                    reqBytes.Add(0x00);// Data部分字节长度

                    // 拼接S7-Parameter
                    reqBytes.AddRange(paramBytes);

                    ushort len = (ushort)reqBytes.Count;
                    byte[] lenBytes = BitConverter.GetBytes(len);// 小端
                    reqBytes[2] = lenBytes[1];
                    reqBytes[3] = lenBytes[0];

//#if DEBUG
//                    Console.WriteLine(string.Join(" ", reqBytes.Select(s => s.ToString("X2"))));
//#endif

                    BodaResult<byte> dataResult = _commUnit.SendAndReceive(reqBytes);
                    if (!dataResult.IsSuccess)
                    {
                        result.IsSuccess = false;
                        result.Message = dataResult.Message;
                        return result;
                    }

//#if DEBUG
//                    Console.WriteLine(string.Join(" ", dataResult.Datas.Select(s => s.ToString("X2"))));
//#endif
                    // 解析
                    if (dataResult.Datas[17] != 0x00)
                    {
                        result.IsSuccess = false;
                        result.Message = "响应报文异常";
                        return result;
                    }
                    else
                    {
                        // 解析所有Item结果
                        index = 21;
                        foreach (var g in groupResult.Datas)
                        {
                            // 这里意思：根据临时对象列表，对应实际请求的集合对象
                            var item = parameters.FirstOrDefault(p => p.Id == g.Id);
                            if (item == null)
                            {
                                result.IsSuccess = false;
                                result.Message = $"【Read】数据解析出错Id：{g.Id}";
                                return result;
                            }

                            if (dataResult.Datas[index] != 0xff)
                            {
                                item.Status = false;
                                item.Error = S7Errors.DataItemCode[dataResult.Datas[index]];

                                result.IsSuccess = false;
                                result.Message = item.Error;
                                return result;
                            }
                            else
                            {
                                // 拿到多少个位
                                ushort byteCount = BitConverter.ToUInt16(new byte[] { dataResult.Datas[index + 3], dataResult.Datas[index + 2] });

                                // BYTE/Word/DWord字节数计数方式
                                // Bit，Char按照具体去读取
                                if (item.PValueSize != S7PValueSize.BIT &&
                                    item.PValueSize != S7PValueSize.CHAR &&
                                    item.PValueSize != S7PValueSize.REAL)
                                {
                                    byteCount /= 8;
                                }

                                // 获取数据的字节数组
                                //List<byte> dataBytes = dataResult.Datas.GetRange(25, bc);
                                var dataBytes = dataResult.Datas.GetRange(index + 4, byteCount);

                                // 返回原始字节数组
                                item.RawBytes = dataBytes;

                                if (item.DataType == typeof(bool))
                                {
                                    dataBytes.ForEach(d =>
                                    {
                                        // d==字节
                                        item.Datas.Add(d == 0x01);
                                    });
                                }
                                else if (item.DataType == typeof(string))
                                {
                                    // 小问题，返回byte数组，用户自己处理字符串数据，有可能两个字节的数组不需要
                                    item.Datas.Add(Encoding.UTF8.GetString(dataBytes.ToArray()));
                                }
                                else
                                {
                                    // 需要的数据类型，对应的多少个字节
                                    int size = Marshal.SizeOf(item.DataType);
                                    for (int i = 0; i < byteCount; i += size)
                                    {
                                        if (item.DataType == typeof(byte))
                                            item.Datas.Add(dataBytes[i]);
                                        else
                                        {
                                            List<byte> temp = dataBytes.GetRange(i, size);
                                            temp.Reverse();

                                            //反射处理数据 16位   32位    64   Float
                                            Type tBitConverter = typeof(BitConverter);
                                            MethodInfo method = tBitConverter.GetMethods(BindingFlags.Public | BindingFlags.Static)
                                                .FirstOrDefault(mi => mi.ReturnType == item.DataType && mi.GetParameters().Count() == 2)
                                                as MethodInfo ?? throw new Exception("获取数据转换的方法错误");

                                            item.Datas.Add(method?.Invoke(tBitConverter, new object[] { temp.ToArray(), 0 })
                                                 ?? throw new Exception("数据转换的方法Invoke错误，可能方法为空"));
                                        }
                                    }
                                }

                                // 下标向后移返回的数据字节数
                                index += byteCount;
                                // 如果是奇数，且不是最后一个数据，则加1的Fill Byte
                                index += (byteCount % 2);  // 需要判断最后一个吗？不需要判断，因为最后就算加1，也不会循环了
                            }

                            // 下标后移固定3个字节（1字节:TransportSize   2Byte:数据响应长度）
                            index += 3;
                            // 前面一步+3: 位置在当前Item的最后一个字节
                            // 后面一步+1: 进入下一个Item的第一个字节, 第一个字节数据一般是0xFF;
                            index += 1;// uedm
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.Message = "【Read】读取数据错误: " + ex.Message;
            }

            return result;
        }

        /// <summary>
        /// 地址解析，建议尽量与博途、Step7保持一致
        ///       位(bit)      字节(Byte)       字（Word）     双字（DWord）
        /// I     I0.0         IB0              IW0            ID0
        /// Q     Q0.0         QB0
        /// M     M0.0         MB0
        /// V     V0.0         VB0
        /// DB    DB1.DBX0.0   DB1.DBB110       DBW           DBD
        /// 
        /// 注意：字符串大小写问题，自行处理
        /// </summary>
        /// <param name="variable"></param>
        /// <returns></returns>
        private BodaResult<S7DataParameter> AnalysisAddress(string variable)
        {
            BodaResult<S7DataParameter> result = new BodaResult<S7DataParameter>();

            if (string.IsNullOrEmpty(variable))
            {
                result.IsSuccess = false;
                result.Message = "地址参数为空";
                return result;
            }
            variable = variable.Trim();

            try
            {
                S7DataParameter parameter = new S7DataParameter();
                // Areas area =
                // ushort db = a
                // PValueSize pV
                // int byteAddr
                // byte bitAddr

                string str = variable.Substring(0, 2);
                if (str.ToUpperInvariant() == "DB")
                {
                    string[] arrays = variable.Split('.');
                    // [0]  DB1  ----   DB100   DB2000
                    // [1]  DBX100
                    // [2]  0

                    // 处理区域类型
                    parameter.Area = S7Areas.DataBlock;

                    // 处理DB编号，DB1/DB100/DB1000，从第2个字符开始，后面都是地址
                    if (ushort.TryParse(arrays[0].Substring(2), out ushort db))
                    {
                        parameter.DBNumber = db;
                    }
                    else
                    {
                        result.IsSuccess = false;
                        result.Message = "DB编号无法解析";
                        return result;
                    }
                    // 处理字节地址
                    if (int.TryParse(arrays[1].Substring(3), out int byteAddr))
                    {
                        parameter.ByteAddress = byteAddr;
                    }
                    else
                    {
                        result.IsSuccess = false;
                        result.Message = "字节位置无法解析";
                        return result;
                    }

                    // 处理参数类型，比如：DB1.DBX1.1中的X
                    string typeStr = arrays[1].Substring(2, 1);// X  B  W  D  R
                    if (!_pValudDic.ContainsKey(typeStr))
                    {
                        result.IsSuccess = false;
                        result.Message = "数据类型无法解析";
                        return result;
                    }
                    parameter.PValueSize = _pValudDic[typeStr];

                    // 处理位地址,比如：DBX1.DBW1.1，按照.分割位3部分
                    if (arrays.Length == 3)
                    {
                        if (typeStr == "X")
                        {
                            if (Byte.TryParse(arrays[2], out byte bitAddr))
                            {
                                if (bitAddr > 7)
                                {
                                    result.IsSuccess = false;
                                    result.Message = "位地址无法解析";
                                    return result;
                                }
                                parameter.BitAddress = bitAddr;
                            }
                            else
                            {
                                result.IsSuccess = false;
                                result.Message = "位地址无法解析";
                                return result;
                            }
                        }
                        else
                        {
                            result.IsSuccess = false;
                            result.Message = "地址无法解析";
                            return result;
                        }

                    }
                }
                else if ("IQMVTC".Contains(variable[0]))
                {
                    //  以下部分所有可能的异常判断   自行处理

                    string[] arrays = variable.Split('.');

                    var positionType = arrays[0][0];
                    if (!_areasDic.Keys.Contains(positionType.ToString()))
                    {
                        result.IsSuccess = false;
                        result.Message = $"不存在寄存器类型:{positionType}";
                        return result;
                    }

                    // [0]  I0      IB0
                    // [1]  0       --
                    parameter.Area = _areasDic[positionType.ToString()];// 判断下是否存在
                    if (positionType == 'V') parameter.DBNumber = 1;

                    if (arrays.Length == 1)
                    {
                        // 需要判断是否存在
                        var valType = arrays[0][1];
                        if (!_pValudDic.Keys.Contains(valType.ToString()))
                        {
                            result.IsSuccess = false;
                            result.Message = $"不存在寄存器地址类型:{valType}";
                            return result;
                        }

                        parameter.PValueSize = _pValudDic[valType.ToString()];
                        if (arrays[0].Length < 3)
                        {
                            result.IsSuccess = false;
                            result.Message = $"寄存器地址长度为：{arrays[0].Length},不符合要求";
                            return result;
                        }

                        parameter.ByteAddress = int.Parse(arrays[0].Substring(2));
                    }
                    else if (arrays.Length == 2)// 位处理
                    {
                        parameter.PValueSize = S7PValueSize.BIT;
                        if (arrays[0].Length < 2)
                        {
                            result.IsSuccess = false;
                            result.Message = $"寄存器地址长度为：{arrays[0].Length},不符合要求";
                            return result;
                        }
                        parameter.ByteAddress = int.Parse(arrays[0].Substring(1));

                        parameter.BitAddress = Byte.Parse(arrays[1]);
                    }
                    else
                    {
                        result.IsSuccess = false;
                        result.Message = "地址无法解析";
                        return result;
                    }
                }
                else
                {
                    result.IsSuccess = false;
                    result.Message = "地址无法解析";
                    return result;
                }

                result.Datas.Add(parameter);
            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.Message = ex.Message;
            }
            return result;
        }

        /// <summary>
        /// 一次请求一个连续区域
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="variable"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public BodaResult<T> Read<T>(string variable, ushort count)
        {
            // string => 实际地址的解析
            var addrResult = AnalysisAddress(variable);
            if (!addrResult.IsSuccess) return new BodaResult<T> { IsSuccess = false, Message = addrResult.Message };

            S7Areas area = addrResult.Datas[0].Area;
            ushort db = addrResult.Datas[0].DBNumber;
            S7PValueSize pValueSize = addrResult.Datas[0].PValueSize;
            int byteAddr = addrResult.Datas[0].ByteAddress;
            byte bitAddr = addrResult.Datas[0].BitAddress;

            return Read<T>(area, db, pValueSize, byteAddr, count, bitAddr);
        }


        #region 写

        public BodaResult Write(string address, byte[] value)
        {
            BodaResult bodaResult = new BodaResult();
            BodaResult<S7DataParameter>? addrResult = AnalysisAddress(address);
            if (addrResult.IsSuccess)
            {
                foreach (S7DataParameter item in addrResult.Datas)
                {
                    var dataType = _pValudTypeDic[item.PValueSize];
                    item.DataType = dataType;
                    // 写入时处理
                    item.DValueSize = S7DValueSize.BWD;
                    Type tBitConverter = typeof(BitConverter);
                    MethodInfo method = tBitConverter.GetMethods(BindingFlags.Public | BindingFlags.Static)
                        .FirstOrDefault(mi => mi.ReturnType == dataType && mi.GetParameters().Count() == 2)
                        as MethodInfo ?? throw new Exception("获取数据转换的方法错误");
                    var targetVal = method?.Invoke(tBitConverter, new object[] { value, 0 });
                    item.Datas.Add(targetVal ?? throw new Exception("数据转换的方法Invoke错误，可能方法为空"));
                }
                bodaResult = Write(addrResult.Datas);
            }
            else
            {
                bodaResult.IsSuccess = false;
                bodaResult.Message = addrResult.Message;
            }
            return bodaResult;
        }

        public BodaResult Write(List<S7DataParameter> parameters)
        {
            BodaResult result = new BodaResult();
            result.IsSuccess = true;
            // 检查PDU长度  尝试自行处理    作业 

            try
            {
                List<byte> reqBytes = new List<byte>();

                #region 基础字节
                // TPKT
                reqBytes.Add(0x03);
                reqBytes.Add(0x00);
                // 初始化无意义，只做点位，后续做修改
                reqBytes.Add(0x00);
                reqBytes.Add(0x00); // 注意下后台再修改---
                // COTP
                reqBytes.Add(0x02);
                reqBytes.Add(0xf0);
                reqBytes.Add(0x80); // 1000 0000
                // S7-Header
                reqBytes.Add(0x32);
                reqBytes.Add(0x01); // ROSCTR

                reqBytes.Add(0x00);
                reqBytes.Add(0x00);
                reqBytes.Add(0x00);
                reqBytes.Add(0x00);
                #endregion

                #region parameter以及data
                // 根据当前parameter参数进行组合
                List<byte> paramList = ParameterItemMulit(parameters);
                ushort pl = (ushort)paramList.Count;
                byte[] plenBytes = BitConverter.GetBytes(pl);
                reqBytes.Add(plenBytes[1]);
                reqBytes.Add(plenBytes[0]);// 处理Parameter字节长度

                //根据当前parameter参数进行组合
                List<byte> dataList = DataItemMulit(parameters);
                ushort dl = (ushort)dataList.Count;
                byte[] dlenBytes = BitConverter.GetBytes(dl);
                reqBytes.Add(dlenBytes[1]);
                reqBytes.Add(dlenBytes[0]);// 处理Data字节长度

                // 拼接Parameter部分字节
                reqBytes.AddRange(paramList);
                // 拼接Data部分字节
                reqBytes.AddRange(dataList);
                #endregion


                // 修改整个报文长度数据
                ushort len = (ushort)reqBytes.Count;
                byte[] lenBytes = BitConverter.GetBytes(len);
                reqBytes[2] = lenBytes[1];
                reqBytes[3] = lenBytes[0];

                BodaResult<byte> dataResult = _commUnit.SendAndReceive(reqBytes);
                if (!dataResult.IsSuccess)
                {
                    result.IsSuccess = false;
                    result.Message = dataResult.Message;
                }

                if (dataResult.Datas[17] != 0x00)
                {
                    result.IsSuccess = false;
                    result.Message = "响应报文异常";
                }
                else
                {
                    int byteIndex = 21;
                    foreach (var item in parameters)
                    {
                        if (dataResult.Datas[byteIndex] != 0xff)
                        {
                            item.Status = false;
                            item.Error = S7Errors.DataItemCode[dataResult.Datas[byteIndex]];
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.Message = ex.Message;
            }

            return result;
        }

        private List<byte> ParameterItemMulit(List<S7DataParameter> parameters)
        {
            List<byte> items = new List<byte>();
            items.Add(0x05);//功能码：写入动作
            items.Add((byte)parameters.Count);// Items的个数  Data的Item个数据与Parameter的Item个数匹配

            foreach (var item in parameters)
            {
                items.Add(0x12);

                items.Add(0x0a);
                items.Add(0x10);
                items.Add((byte)item.PValueSize);// 类型 02 Byte   03  Char   04 Word   06  DWord

                ushort vcount = (ushort)item.Datas.Count;
                items.Add(BitConverter.GetBytes(vcount)[1]);
                items.Add(BitConverter.GetBytes(vcount)[0]);// 写一个值

                items.Add(BitConverter.GetBytes(item.DBNumber)[1]);
                items.Add(BitConverter.GetBytes(item.DBNumber)[0]);

                items.Add((byte)item.Area);  //V

                // 地址计算
                int byteAddr = item.ByteAddress;
                byte bitAddr = item.BitAddress;
                byteAddr = (byteAddr << 3) + bitAddr;

                items.Add((byte)(byteAddr / 256 / 256 % 256));
                items.Add((byte)(byteAddr / 256 % 256));
                items.Add((byte)(byteAddr % 256));
            }

            return items;
        }

        private List<byte> DataItemMulit(List<S7DataParameter> parameters)
        {
            // VB10   Byte  
            List<byte> items = new List<byte>();

            foreach (var item in parameters)
            {
                items.Add(0x00);
                items.Add((byte)item.DValueSize);//Transport size: Byte/Word/DWord

                // Count需要与Size匹配
                int size = Marshal.SizeOf(item.DataType);
                // bit  char     other * 8
                ushort vcount = (ushort)(item.Datas.Count * size * 8);
                if (item.PValueSize == S7PValueSize.BIT)
                    vcount = (ushort)item.Datas.Count;
                items.Add(BitConverter.GetBytes(vcount)[1]);
                items.Add(BitConverter.GetBytes(vcount)[0]);// 写入的位数

                if (item.PValueSize == S7PValueSize.BIT)
                {
                    item.Datas.ForEach(d => items.Add((byte)(bool.Parse(d.ToString()) ? 0x01 : 0x00)));
                }
                else
                {
                    item.Datas.ForEach(d =>
                    {
                        dynamic v = d;
                        byte[] vBytes = BitConverter.GetBytes(v);
                        items.AddRange(vBytes);
                    });
                }
                if (item.Count % 2 != 0)
                    items.Add(0x00);// fill byte
            }
            return items;
        }
        #endregion


        #region PLC启/停
        public BodaResult Run()
        {
            BodaResult result = new BodaResult();

            string s = "P_PROGRAM";
            byte[] sb = Encoding.ASCII.GetBytes(s);

            List<byte> runBytes = new List<byte>{
                // TPKT
                0x03,0x00,0x00,0x25,
                    // COTP
                0x02,0xf0,0x80,
                // Header
                0x32,0x01,
                0x00,0x00,0x00,0x00,
                // PL
                0x00,0x14,
                // DL
                0x00,0x00,
                // Parameter
                0x28,  // 启动标识
                0x00,0x00,0x00,0x00,0x00,0x00,0xfd,
                0x00, 0x00,
                0x09,
                // P_PROGRAM  9个字符对应的16进制Ascii值
                0x50,0x5f,0x50,0x52,0x4f,0x47,0x52,0x41,0x4d
            };

            BodaResult<byte> dataResult = _commUnit.SendAndReceive(runBytes);
            if (!dataResult.IsSuccess)
            {
                result.IsSuccess = false;
                result.Message = dataResult.Message;
            }
            else if (dataResult.Datas[17] != 0x00)
            {
                result.IsSuccess = false;
                result.Message = "响应报文异常";
            }

            return result;
        }

        public BodaResult Stop()
        {
            BodaResult result = new BodaResult();

            List<byte> stopBytes = new List<byte> {
                // TPKT
                0x03,0x00,0x00,0x21,
                    // COTP
                0x02,0xf0,0x80,
                // Header
                0x32,0x01,
                0x00,0x00,0x00,0x00,
                // PL
                0x00,0x10,
                // DL
                0x00,0x00,
                // Parameter
                0x29,// Stop标识
                0x00,0x00,0x00,0x00,0x00,
                0x09,
                // P_PROGRAM  9个字符对应的16进制Ascii值
                0x50,0x5f,0x50,0x52,0x4f,0x47,0x52,0x41,0x4d
            };

            BodaResult<byte> dataResult = _commUnit.SendAndReceive(stopBytes);
            if (!dataResult.IsSuccess)
            {
                result.IsSuccess = false;
                result.Message = dataResult.Message;
            }
            else if (dataResult.Datas[17] != 0x00)
            {
                result.IsSuccess = false;
                result.Message = "响应报文异常";
            }

            return result;
        }
        #endregion

        #region 时间读写
        public BodaResult<DateTime> ReadTime()
        {
            BodaResult<DateTime> result = new BodaResult<DateTime>();

            List<byte> readTimeBytes = new List<byte> {
                // TPKT
                0x03,0x00,0x00,0x1d,
                    // COTP
                0x02,0xf0,0x80,
                // Header
                0x32,0x07, // UserData
                0x00,0x00,0x00,0x00,
                // PL
                0x00,0x08,
                // DL
                0x00,0x04,

                // Parameter
                0x00,0x01,0x12,

                0x04,// Parameter中当前字节后的字节数
                0x11, 0x47,
                0x01,// SubFunction  Read Clock
                0x00,
                // Data
                0x0a,0x00,0x00,0x00
            };
            BodaResult<byte> dataResult = _commUnit.SendAndReceive(readTimeBytes);
            if (!dataResult.IsSuccess)
            {
                result.IsSuccess = false;
                result.Message = dataResult.Message;
            }
            else if (dataResult.Datas[27] != 0x00 || dataResult.Datas[28] != 0x00)
            {
                result.IsSuccess = false;
                result.Message = "响应报文异常";
            }
            else if (dataResult.Datas[29] != 0xff)
            {
                result.IsSuccess = false;
                result.Message = S7Errors.DataItemCode[dataResult.Datas[29]];
            }
            else
            {
                // 年
                byte yearByte = dataResult.Datas[35];  // 0x22  ->  34
                int yearInt = int.Parse(yearByte.ToString("x"));
                if (yearInt >= 90)
                    yearInt += 1900;
                else
                    yearInt += 2000;
                // 月
                byte monthByte = dataResult.Datas[36];
                int monthInt = int.Parse(monthByte.ToString("x"));
                // 日
                byte dayByte = dataResult.Datas[37];
                int dayInt = int.Parse(dayByte.ToString("x"));
                // 时
                byte hourByte = dataResult.Datas[38];
                int hourInt = int.Parse(hourByte.ToString("x"));
                // 分
                byte minuteByte = dataResult.Datas[39];
                int minuteInt = int.Parse(minuteByte.ToString("x"));
                // 秒
                byte secondByte = dataResult.Datas[40];
                int secondInt = int.Parse(secondByte.ToString("x"));

                DateTime dt = new DateTime(yearInt, monthInt, dayInt, hourInt, minuteInt, secondInt);
                result.Datas.Add(dt);
            }

            return result;
        }

        public BodaResult WriteTime(DateTime dt)
        {
            BodaResult result = new BodaResult();

            // 2022
            string yearStr = dt.Year.ToString(); //   22  ->  "22"   ->   0x22
            byte[] bs = Convert.FromHexString("313233");
            // { 0x31 0x32 0x33}    串口通信助手    string         01 03 00 00 00 0A 0d 00
            byte year = Convert.FromHexString(yearStr)[1];  // 0x20 0x22
            // 6 -> string   "06"   
            byte month = Convert.FromHexString(dt.Month.ToString("00"))[0];
            byte day = Convert.FromHexString(dt.Day.ToString("00"))[0];
            byte hour = Convert.FromHexString(dt.Hour.ToString("00"))[0];
            byte minute = Convert.FromHexString(dt.Minute.ToString("00"))[0];
            byte second = Convert.FromHexString(dt.Second.ToString("00"))[0];

            List<byte> msBytes = new List<byte>(Convert.FromHexString(dt.Millisecond.ToString("0000")));
            msBytes.Reverse();
            ushort millisecond = BitConverter.ToUInt16(msBytes.ToArray());
            millisecond = (ushort)((millisecond << 4) + dt.DayOfWeek + 1);
            //millisecond = (ushort)((millisecond << 4) + 1);// 星期日
            //millisecond << 4;
            //dt.DayOfWeek

            List<byte> writeTimeBytes = new List<byte> {
                // TPKT
                0x03,0x00,0x00,0x27,
                    // COTP
                0x02,0xf0,0x80,
                // Header
                0x32,0x07, // UserData
                0x00,0x00,0x00,0x00,
                // PL
                0x00,0x08,
                // DL
                0x00,0x0e,
                // Parameter
                0x00,0x01,0x12,
                0x04,// Parameter中当前字节后的字节数
                0x11, 0x47,
                0x02,// SubFunction  Set Clock
                0x00,
                // Data
                0xff,0x09,0x00,0x0a,
                // 时间  0-89  2000-2089    90-99   1990-1999
                // 2021-05-11 21:55:00.000 周二
                0x00,0x19,year,month,day,hour,minute,second,
                (byte)(millisecond/256%256),
                (byte)(millisecond%256)
            };

            BodaResult<byte> dataResult = _commUnit.SendAndReceive(writeTimeBytes);
            if (!dataResult.IsSuccess)
            {
                result.IsSuccess = false;
                result.Message = dataResult.Message;
            }
            else if (dataResult.Datas[27] != 0x00 || dataResult.Datas[28] != 0x00)
            {
                result.IsSuccess = false;
                result.Message = "响应报文异常";
            }
            else if (dataResult.Datas[29] != 0xff)
            {
                result.IsSuccess = false;
                result.Message = S7Errors.DataItemCode[dataResult.Datas[29]];
            }

            return result;
        }

        public BodaResult SendMsgc(byte[] bytes, int timeout = 1500)
        {
            throw new NotImplementedException();
        }

        public BodaResult<byte> ReceiveMsg(int timeout = 2000, int bytesLen = 1024)
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}
